package com.hys.app.service.system.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.hys.app.model.system.dto.DeptCreateDTO;
import com.hys.app.model.system.dto.DeptQueryParams;
import com.hys.app.model.system.dto.DeptUpdateDTO;
import com.hys.app.converter.system.DeptConverter;
import com.hys.app.framework.cache.Cache;
import com.hys.app.framework.exception.ServiceException;
import com.hys.app.framework.database.mybatisplus.base.BaseServiceImpl;
import com.hys.app.mapper.system.DeptMapper;
import com.hys.app.model.base.CachePrefix;
import com.hys.app.model.base.context.Region;
import com.hys.app.model.system.dos.DeptDO;
import com.hys.app.service.erp.WarehouseManager;
import com.hys.app.service.system.AdminUserManager;
import com.hys.app.service.system.DeptManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.util.*;

import static com.hys.app.framework.util.CollectionUtils.convertSet;

/**
 * 部门 Service 实现类
 *
 *
 */
@Service
@Validated
@Slf4j
public class DeptManagerImpl extends BaseServiceImpl<DeptMapper, DeptDO> implements DeptManager {

    @Autowired
    private DeptMapper deptMapper;

    @Autowired
    private Cache cache;

    @Autowired
    private AdminUserManager adminUserManager;

    @Autowired
    private DeptConverter converter;

    @Autowired
    @Lazy
    private WarehouseManager warehouseManager;

    @Override
    public Long createDept(DeptCreateDTO reqVO, Region region) {
        // 校验正确性
        if (reqVO.getParentId() == null) {
            reqVO.setParentId(DeptDO.ROOT_ID);
        }
        validateForCreateOrUpdate(null, reqVO.getParentId(), reqVO.getName());
        // 插入部门
        DeptDO dept = converter.convert(reqVO, region);
        deptMapper.insert(dept);
        cache.remove(CachePrefix.DEPT_CHILDREN_ID_LIST.getPrefix());
        return dept.getId();
    }

    @Override
    public void updateDept(DeptUpdateDTO reqVO, Region region) {
        // 校验正确性
        if (reqVO.getParentId() == null) {
            reqVO.setParentId(DeptDO.ROOT_ID);
        }
        validateForCreateOrUpdate(reqVO.getId(), reqVO.getParentId(), reqVO.getName());
        // 更新部门
        DeptDO updateObj = converter.convert(reqVO, region);
        deptMapper.updateById(updateObj);
        cache.remove(CachePrefix.DEPT_CHILDREN_ID_LIST.getPrefix());
    }

    @Override
    public void deleteDept(Long id) {
        // 校验是否存在
        validateDeptExists(id);
        // 校验是否有子部门
        if (deptMapper.selectCountByParentId(id) > 0) {
            throw new ServiceException("存在子部门，无法删除");
        }
        Long count = adminUserManager.countByDeptId(id);
        if(count > 0){
            throw new ServiceException("部门下存在用户，无法删除");
        }
        if(warehouseManager.countByDeptId(id) > 0){
            throw new ServiceException("部门下存在仓库，无法删除");
        }
        // 删除部门
        deptMapper.deleteById(id);
        cache.remove(CachePrefix.DEPT_CHILDREN_ID_LIST.getPrefix());
    }

    private void validateForCreateOrUpdate(Long id, Long parentId, String name) {
        // 校验自己存在
        validateDeptExists(id);
        // 校验父部门的有效性
        validateParentDept(id, parentId);
        // 校验部门名的唯一性
        validateDeptNameUnique(id, parentId, name);
    }

    private void validateDeptExists(Long id) {
        if (id == null) {
            return;
        }
        DeptDO dept = deptMapper.selectById(id);
        if (dept == null) {
            throw new ServiceException("当前部门不存在");
        }
    }

    private void validateParentDept(Long id, Long parentId) {
        if (parentId == null || DeptDO.ROOT_ID.equals(parentId)) {
            return;
        }
        // 不能设置自己为父部门
        if (parentId.equals(id)) {
            throw new ServiceException("不能设置自己为父部门");
        }
        // 父岗位不存在
        DeptDO dept = deptMapper.selectById(parentId);
        if (dept == null) {
            throw new ServiceException("父级部门不存在");
        }
        // 父部门不能是原来的子部门
        List<DeptDO> children = getChildDeptList(id);
        if (children.stream().anyMatch(dept1 -> dept1.getId().equals(parentId))) {
            throw new ServiceException("不能设置自己的子部门为父部门");
        }
    }

    private void validateDeptNameUnique(Long id, Long parentId, String name) {
        DeptDO dept = deptMapper.selectByParentIdAndName(parentId, name);
        if (dept == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的岗位
        if (id == null) {
            throw new ServiceException("已经存在该名字的部门");
        }
        if (ObjectUtil.notEqual(dept.getId(), id)) {
            throw new ServiceException("已经存在该名字的部门");
        }
    }

    @Override
    public DeptDO getDept(Long id) {
        return deptMapper.selectById(id);
    }

    @Override
    public List<DeptDO> getDeptList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return deptMapper.selectBatchIds(ids);
    }

    @Override
    public List<DeptDO> getDeptList(DeptQueryParams reqVO) {
        return deptMapper.selectList(reqVO);
    }

    @Override
    public List<DeptDO> getChildDeptList(Long parentId) {
        List<DeptDO> children = new LinkedList<>();
        // 遍历每一层
        Collection<Long> parentIds = Collections.singleton(parentId);
        for (int i = 0; i < Short.MAX_VALUE; i++) { // 使用 Short.MAX_VALUE 避免 bug 场景下，存在死循环
            // 查询当前层，所有的子部门
            List<DeptDO> depts = deptMapper.selectListByParentId(parentIds);
            // 1. 如果没有子部门，则结束遍历
            if (CollUtil.isEmpty(depts)) {
                break;
            }
            // 2. 如果有子部门，继续遍历
            children.addAll(depts);
            parentIds = convertSet(depts, DeptDO::getId);
        }
        return children;
    }

    @Override
//    @DataPermission(enable = false) // 禁用数据权限，避免建立不正确的缓存
    public Set<Long> getChildDeptIdListFromCache(Long parentId) {
        Set<Long> ids = (Set<Long>) cache.getHash(CachePrefix.DEPT_CHILDREN_ID_LIST.getPrefix(), parentId);
        if(ids == null){
            List<DeptDO> children = getChildDeptList(parentId);
            ids = convertSet(children, DeptDO::getId);
            cache.putHash(CachePrefix.DEPT_CHILDREN_ID_LIST.getPrefix(), parentId, ids);
        }
        return ids;
    }

}
